/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.spi.entity.info.propertyinfo;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.inspur.edp.cef.api.manager.serialize.CefSerializeContext;
import com.inspur.edp.cef.api.repository.GspDbDataType;
import com.inspur.edp.cef.entity.entity.FieldType;
import com.inspur.edp.cef.spi.entity.PropertyDefaultValue;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.cef.spi.jsonser.util.SerializerUtil;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;


public class DataTypePropertyInfo {

    private String propertyName;
    private String displayValueKey;
    private boolean required = false;
    private boolean enableRtrim = true;
    //    private boolean requiredAsConstraint = true;
//    private boolean isDefaultNull = false;
    private int length = 0;
    private int precision = 0;
    private FieldType fieldType = FieldType.forValue(0);
    private ObjectType objectType = ObjectType.Normal;
    private BasePropertyInfo objectInfo;
    private PropertyDefaultValue defaultValue;
    private boolean isMultiLaguange = false;
    private boolean bigNumber = false;
    private GspDbDataType dbDataType;
    private String dbColumnName;

    public DataTypePropertyInfo(
        String propertyName,
        String displayValueKey,
        boolean required,
        boolean enableRtrim,
//            boolean requiredAsConstraint,
//            boolean isDefaultNull,
        int length,
        int precision,
        FieldType fieldType,
        ObjectType objectType,
        BasePropertyInfo objectInfo,
        PropertyDefaultValue defaultValue) {

        this.propertyName = propertyName;
        this.displayValueKey = displayValueKey;
        this.required = required;
        this.enableRtrim = enableRtrim;
//        this.requiredAsConstraint = requiredAsConstraint;
//        this.isDefaultNull = isDefaultNull;
        this.length = length;
        this.precision = precision;
        this.fieldType = fieldType;
        this.objectType = objectType;
        this.objectInfo = objectInfo;
        this.defaultValue = defaultValue;
    }

    public DataTypePropertyInfo(
        String propertyName,
        String displayValueKey,
        boolean required,
        boolean enableRtrim,
//            boolean requiredAsConstraint,
//            boolean isDefaultNull,
        int length,
        int precision,
        FieldType fieldType,
        ObjectType objectType,
        BasePropertyInfo objectInfo,
        PropertyDefaultValue defaultValue, boolean isMultiLaguange) {

        this(propertyName, displayValueKey, required, enableRtrim, length, precision, fieldType,
            objectType, objectInfo, defaultValue);
        this.isMultiLaguange = isMultiLaguange;
    }

    public DataTypePropertyInfo(
        String propertyName,
        String displayValueKey,
        boolean required,
        boolean enableRtrim,
        int length,
        int precision,
        FieldType fieldType,
        ObjectType objectType,
        BasePropertyInfo objectInfo,
        PropertyDefaultValue defaultValue,
        boolean isMultiLaguange,
        boolean bigNumber) {

        this(propertyName, displayValueKey, required, enableRtrim, length, precision, fieldType,
            objectType, objectInfo, defaultValue, isMultiLaguange);
        this.bigNumber = bigNumber;
    }

    public String getPropertyName() {
        return propertyName;
    }

    public String getDisplayValueKey() {
        return displayValueKey;
    }


    public boolean getRequired() {
        return required;
    }

    public boolean getEnableRtrim() {
        return enableRtrim;
    }

//    public boolean getRequiredAsConstraint() {
//        return requiredAsConstraint;
//    }
//
//    public boolean isDefaultNull() {
//        return isDefaultNull;
//    }

    public int getLength() {
        return length;
    }

    public int getPrecision() {
        return precision;
    }

    public FieldType getFieldType() {
        return fieldType;
    }

    public ObjectType getObjectType() {
        return objectType;
    }

    public Object getObjectInfo() {
        return objectInfo;
    }

    public PropertyDefaultValue getDefaultValue() {
        return defaultValue;
    }

    public void write(JsonGenerator writer, Object value, SerializerProvider serializer,
        CefSerializeContext serContext) {

        if (objectType != ObjectType.Normal) {
            if (bigNumber) {
                serContext.setBigNumber(bigNumber);
            }
            objectInfo.write(writer, propertyName, value, serializer, serContext);
        } else {
            writeBasicTypeProp(writer, value, serializer, serContext);
        }
    }

    public void write(String prefix, JsonGenerator writer, Object value, SerializerProvider serializer,
                      CefSerializeContext serContext) {

        if (objectType != ObjectType.Normal) {
            if (bigNumber) {
                serContext.setBigNumber(bigNumber);
            }
            String propName = StringUtils.isEmpty(prefix) ? propertyName : prefix + "_" + propertyName;
            objectInfo.write(writer, propName, value, serializer, serContext);
        } else {
            writeBasicTypeProp(prefix, writer, value, serializer, serContext);
        }
    }

    private void writeBasicTypeProp(String prefix, JsonGenerator writer, Object value,
                                    SerializerProvider serializer, CefSerializeContext serContext) {
        String prop = StringUtils.isEmpty(prefix) ? propertyName : prefix + "_" + propertyName;
        switch (fieldType) {
            case String:
            case Text:
                SerializerUtil.writeString(writer, value, prop, serializer);
                break;
            case Integer:
                SerializerUtil.writeInt(writer, value, prop, serializer);
                break;
            case Decimal:
                writeDecimalDataTypeInfo(writer, value, serializer, serContext, prop);
                break;
            case Date:
                SerializerUtil.writeDate(writer, value, prop, serializer);
                break;
            case DateTime:
//                SerializerUtil.writeStdDateTime(writer, value, propertyName, serializer);
                SerializerUtil.writeDateTime(writer, value, prop, serializer, serContext.getEnableStdTimeFormat());
                break;
            case Boolean:
                SerializerUtil.writeBool(writer, value, prop, serializer);
                break;
            case Binary:
                SerializerUtil.writeBytes(writer, value, prop, serializer);
                break;
            default:
                throw new RuntimeException("暂不支持类型为" + fieldType.name() + "的属性序列化");
        }
    }

    private void writeDecimalDataTypeInfo(JsonGenerator writer, Object value, SerializerProvider serializer, CefSerializeContext serContext, String prop) {
        if (!bigNumber && serContext != null) {
            SerializerUtil.writeDecimal(writer, value, prop, serializer,
                    serContext.isBigNumber());
        } else {
            SerializerUtil.writeDecimal(writer, value, prop, serializer, bigNumber);
        }
        return;
    }

    private void writeBasicTypeProp(JsonGenerator writer, Object value,
        SerializerProvider serializer, CefSerializeContext serContext) {
        switch (fieldType) {
            case String:
            case Text:
                SerializerUtil.writeString(writer, value, propertyName, serializer);
                break;
            case Integer:
                SerializerUtil.writeInt(writer, value, propertyName, serializer);
                break;
            case Decimal:
                writeDecimalProp(writer, value, serializer, serContext);
                break;
            case Date:
                SerializerUtil.writeDate(writer, value, propertyName, serializer);
                break;
            case DateTime:
//                SerializerUtil.writeStdDateTime(writer, value, propertyName, serializer);
                SerializerUtil.writeDateTime(writer, value, propertyName, serializer, serContext.getEnableStdTimeFormat());
                break;
            case Boolean:
                SerializerUtil.writeBool(writer, value, propertyName, serializer);
                break;
            case Binary:
                SerializerUtil.writeBytes(writer, value, propertyName, serializer);
                break;
            default:
                throw new RuntimeException("暂不支持类型为" + fieldType.name() + "的属性序列化");
        }
    }

    protected void writeDecimalProp(JsonGenerator writer, Object value, SerializerProvider serializer, CefSerializeContext serContext) {
        if (!bigNumber && serContext != null) {
            SerializerUtil.writeDecimal(writer, value, propertyName, serializer,
                serContext.isBigNumber());
        } else {
            SerializerUtil.writeDecimal(writer, value, propertyName, serializer, bigNumber);
        }
    }


    public Object read(JsonParser reader, DeserializationContext serializer,
        CefSerializeContext serContext) {

        if (objectType != ObjectType.Normal) {
            return objectInfo.read(reader, propertyName, serializer, serContext);
        } else {
            return readBasicTypeProp(reader, serializer,serContext);
        }
    }

    public Object read(JsonNode node, CefSerializeContext serContext){
        if (objectType != ObjectType.Normal) {
            return objectInfo.read(node, propertyName, serContext);
        } else {
            return readBasicTypeProp(node, serContext);
        }
    }

    private Object readBasicTypeProp(JsonParser reader, DeserializationContext serializer,CefSerializeContext serContext) {

        if (reader.getCurrentToken() == JsonToken.VALUE_NULL)
            return null;
        switch (fieldType) {
            case String:
            case Text:
                return SerializerUtil.readString(reader);
            case Integer:
                return SerializerUtil.readInt(reader);
            case Decimal:
                return SerializerUtil.readDecimal(reader);
            case Date:
                return SerializerUtil.readDateTime(reader, false);
            case DateTime:
                return SerializerUtil.readDateTime(reader, serContext.getEnableStdTimeFormat());
            case Boolean:
                return SerializerUtil.readBool(reader);
            case Binary:
                return SerializerUtil.readBytes(reader);
            default:
                throw new RuntimeException("暂不支持类型为" + fieldType.name() + "的属性反序列化");
        }

    }
    private Object readBasicTypeProp(JsonNode node, CefSerializeContext serContext) {

        if (node.isNull())
            return null;
        switch (fieldType) {
            case String:
            case Text:
                return node.textValue();
            case Integer:
                return node.intValue();
            case Decimal:
                return node.decimalValue();
            case Date:
                return SerializerUtil.readDateTime(node, false);
            case DateTime:
                return SerializerUtil.readDateTime(node, serContext.getEnableStdTimeFormat());
            case Boolean:
                if(node.getNodeType() == JsonNodeType.STRING){
                    String value = node.textValue();
                    if("0".equals(value))
                        return false;
                    return true;
                }
                return node.booleanValue();
            case Binary:
                try {
                    return node.binaryValue();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            default:
                throw new RuntimeException("暂不支持类型为" + fieldType.name() + "的属性反序列化");
        }

    }

    public void writeChange(JsonGenerator writer, Object value, SerializerProvider serializer,
        CefSerializeContext serContext) {

        if (objectType != ObjectType.Normal) {
            if (bigNumber) {
                serContext.setBigNumber(bigNumber);
            }
            objectInfo.writeChange(writer, propertyName, value, serializer, serContext);
        } else {
            writeBasicTypeProp(writer, value, serializer, serContext);
        }
    }

    public Object readChange(JsonParser reader, DeserializationContext serializer,
        CefSerializeContext serContext) {

        if (objectType != ObjectType.Normal) {
            if(this instanceof RefDataTypePropertyInfo&&objectType==ObjectType.Association)
                return objectInfo.readChange(reader, ((RefDataTypePropertyInfo)this).getRefPropertyName(), serializer, serContext);
            return objectInfo.readChange(reader, propertyName, serializer, serContext);
        } else {
            return readBasicTypeProp(reader, serializer,serContext);
        }
    }

    public Object readChange(JsonNode node, CefSerializeContext serContext){
        if (objectType != ObjectType.Normal) {
            return objectInfo.readChange(node, propertyName, serContext);
        } else {
            return readBasicTypeProp(node,serContext);
        }
    }

    public boolean isMultiLaguange() {
        return isMultiLaguange;
    }

    public boolean getIsBigNumber(){return bigNumber;}
    public void  setIsBigNumber(boolean  value){bigNumber=value;}

    public Object createPropertyDataValue() {
        switch (objectType) {
            case Normal:
                switch (fieldType) {
                    case String:
                    case Text:
                        return "";
                    case Integer:
                        return 0;
                    case Decimal:
                        return new BigDecimal(0);
                    case Date:
                        return new Date();
                    case DateTime:
                        return new Date();
                    case Boolean:
                        return false;
                    case Binary:
                        return null;
                    default:
                        throw new RuntimeException("暂不支持类型为" + fieldType.name() + "的属性反序列化");
                }
            case Association:
                AssocationPropertyInfo assocationPropertyInfo= (AssocationPropertyInfo) objectInfo;
                return assocationPropertyInfo.createAssociationDefaultValue();
            case Enum:
                EnumPropertyInfo enumPropertyInfo= (EnumPropertyInfo) objectInfo;
                return enumPropertyInfo.createAssociationDefaultValue();
            case UDT:
            case DynamicProp:
            default:
                throw new UnsupportedOperationException();
        }
    }

    public void setValue(Object data, String propertyName, Object value, CefSerializeContext serContext){
        if(objectInfo != null)
            objectInfo.setValue(data, propertyName, value, serContext);
    }

    public Object createValue(){
        if(objectInfo != null)
            return objectInfo.createValue();
        throw new UnsupportedOperationException();
    }

    public Object createChange(){
        if(objectInfo != null)
            return objectInfo.createChange();
        throw new UnsupportedOperationException();
    }

    public GspDbDataType getDbDataType() {
        return dbDataType;
    }

    public void setDbDataType(GspDbDataType dbDataType) {
        this.dbDataType = dbDataType;
    }

    public String getDbColumnName() {
        return dbColumnName;
    }

    public void setDbColumnName(String dbColumnName) {
        this.dbColumnName = dbColumnName;
    }

    public  static  void setPropertyInfoByRefed(DataTypePropertyInfo refDataTypePropertyInfo,DataTypePropertyInfo refedDataTypePropInfo)
    {
        refDataTypePropertyInfo.required= refedDataTypePropInfo.required;
        refDataTypePropertyInfo.enableRtrim= refedDataTypePropInfo.enableRtrim;
        refDataTypePropertyInfo.length= refedDataTypePropInfo.length;
        refDataTypePropertyInfo.precision= refedDataTypePropInfo.precision;
        refDataTypePropertyInfo.fieldType= refedDataTypePropInfo.fieldType;
        refDataTypePropertyInfo.objectType=refedDataTypePropInfo.objectType;
        refDataTypePropertyInfo.objectInfo =refedDataTypePropInfo.objectInfo;
        refDataTypePropertyInfo.defaultValue=refedDataTypePropInfo.defaultValue;
        refDataTypePropertyInfo.isMultiLaguange=refedDataTypePropInfo.isMultiLaguange;
        refDataTypePropertyInfo.bigNumber=refedDataTypePropInfo.bigNumber;
    }


    public String getPropValue(Object value){
        String returnvalue = "";
        if (objectType != ObjectType.Normal) {
            returnvalue = objectInfo.getPropValue(value);
        } else {
            returnvalue = getBasicProp(value);
        }
        return returnvalue;
    }



    public String  getBasicProp(Object value){
        String propString = "";
        switch (fieldType) {
            case String:
            case Text:
                propString = "\""+SerializerUtil.getString(value)+"\"";
                break;
            case Integer:
                propString = String.valueOf(SerializerUtil.getInteger(value));
                break;
            case Decimal:
                propString =  String.valueOf(SerializerUtil.getDecimal(value));
                break;
            case Date:
                propString =  "\""+SerializerUtil.getDateProp(value)+"\"";
                break;
            case DateTime:
                propString =  "\""+SerializerUtil.getDatetimeProp(value)+"\"";
                break;
            case Boolean:
                propString =  String.valueOf(SerializerUtil.getBoolean(value));
                break;
            case Binary:
                propString =  String.valueOf(SerializerUtil.getBinary(value));
                break;
            default:
                throw new RuntimeException("暂不支持类型为" + fieldType.name() + "的属性序列化");
        }
        return propString;
    }

}
